#include "types.h"
#include "x86.h"
#include "traps.h"
#include "defs.h"
#include "spinlock.h"
#include "gui.h"

static struct spinlock mouse_lock;

//origin info
static int state = 0;
static int left_down = 0;
static int right_down = 0;
static int x_sign = 0;
static int y_sign = 0;
static int x_overflow = 0;
static int y_overflow = 0;

//mid info
static int left_already_down = 0;
static int right_already_down = 0;
static int left_click_tick = -20;
static int is_dragging = 0;

//final info
static int event = 0;
static int x_position = 0;
static int y_position = 0;
static int x_drag_start = 0;
static int y_drag_start = 0;

void
mouseinit()
{
    outb( 0x64 , 0xa8 );
    outb( 0x64 , 0xd4 );
    outb( 0x60 , 0xf4 );
    outb( 0x64 , 0x60 );
    outb( 0x60 , 0x47 );
    initlock(&mouse_lock,"mouse");
    picenable(IRQ_MOUS);
    ioapicenable(IRQ_MOUS, 0);
}

void
mouseintr(uint tick)
{
	uint ch;
	ch = inb(0x64);
	if((ch & 0x01) == 0)
	{
		//cprintf("no data\n");
		state = 1;
		return;
	}

	acquire(&mouse_lock);
	ch = inb(0x60);

	if(state == 1)
	{
		if((ch & 0x08) == 0 || (ch & 0x04) != 0)
		{
			release(&mouse_lock);
			return;
		}
		left_down = (ch & 0x01) ? 1 : 0;
		right_down = (ch & 0x02) ? 1 : 0;
		x_sign = (ch & 0x10) ? 1 : 0;
		y_sign = (ch & 0x20) ? 1 : 0;
		x_overflow = (ch & 0x40) ? 1 : 0;
		y_overflow = (ch & 0x80) ? 1 : 0;
		state = 2;
		release(&mouse_lock);
		//cprintf("state1\n");
		return;
	}
	else if(state == 2)
	{
		int dis;
		if(x_sign == 0)
			dis = ch;
		else
			dis = ch - 256;
		if(dis > 50 || dis < -50)
		{
			//cprintf("error");
			state = 1;
			release(&mouse_lock);
			return;
		}
		//cprintf("xdis: %d, sign: %d\n", dis, x_sign);
		x_position += dis;
		if(x_position < 0)
			x_position = 0;
		if(x_position > SCREEN_X_MAX - SIZE_X_MOUSE)
			x_position = SCREEN_X_MAX - SIZE_X_MOUSE;
		state = 3;
		release(&mouse_lock);
		//cprintf("state2\n");
		return;
	}
	else if(state == 3)
	{
		int dis;
		if(y_sign == 0)
			dis = ch;
		else
			dis = ch - 256;
		if(dis > 50 || dis < -50)
		{
			//cprintf("error");
			state = 1;
			release(&mouse_lock);
			return;
		}
		//cprintf("ydis: %d, sign: %d, overflow: %d\n", dis, y_sign, y_overflow);
		y_position -= dis;
		if(y_position < 0)
			y_position = 0;
		if(y_position > SCREEN_Y_MAX - SIZE_Y_MOUSE)
			y_position = SCREEN_Y_MAX - SIZE_Y_MOUSE;
		state = 1;
		release(&mouse_lock);
		//cprintf("state3\n");
	}
	else
	{
		state = 1;
		release(&mouse_lock);
		return;
	}

	event = 0;

	if(left_already_down == 0 && right_already_down == 0)
	{
		if(left_down == 1)
		{
			left_already_down = 1;
			x_drag_start = x_position;
			y_drag_start = y_position;
		}
		else if(right_down == 1)
		{
			right_already_down = 1;
		}
	}

	else if(left_already_down == 1)
	{
		if(left_down == 0)
		{
			//cprintf("dragging: %d\n", is_dragging);
			if(is_dragging == 1)
			{
				event = DRAG_RELEASE;
				is_dragging = 0;
			}
			else
			{
				int dtick = tick - left_click_tick;
				//cprintf("tick: %d\n", dtick);
				if(dtick > 15)
				{
					event = LEFT_CLICK;
					left_click_tick = tick;
				}
				else
				{
					event = DOUBLE_CLICK;
					left_click_tick = -20;
				}
			}
			left_already_down = 0;
		}
		else
		{
			event = DRAGGING;
			is_dragging = 1;
		}
	}

	else if(right_already_down == 1)
	{
		if(right_down == 0)
		{
			event = RIGHT_CLICK;
			right_already_down = 0;
		}
	}

	switch(event)
	{
		case LEFT_CLICK:
			cprintf("left click\n");
			break;
		case RIGHT_CLICK:
			cprintf("right click\n");
			break;
		case DOUBLE_CLICK:
			cprintf("double click\n");
			break;
		case DRAGGING:
			cprintf("dragging\n");
			break;
		case DRAG_RELEASE:
			cprintf("drag release\n");
			break;
	}

	mouse_response(event, x_position, y_position, x_drag_start, y_drag_start);
}